package test.transferFile.codec;

import java.nio.charset.Charset;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;

import test.transferFile.common.MyBuffer;
import test.transferFile.common.MyFile;

public class CumulativeTransferFileDecoder extends CumulativeProtocolDecoder {

  private Charset charset;

  public CumulativeTransferFileDecoder(Charset charset) {
    this.charset=charset;
  }

  /**
   * 这个方法的返回值是重点： 
   * 1、当内容刚好时，返回false，告知父类接收下一批内容
   * 2、内容不够时需要下一批发过来的内容，此时返回false，这样父类CumulativeProtocolDecoder会将内容放进IoSession中，等下次来数据后就自动拼装再交给本类的doDecode
   * 3、当内容多时，返回true，因为需要再将本批数据进行读取，父类会将剩余的数据再次推送本类的doDecode
   */
  @Override
  protected boolean doDecode(IoSession session, IoBuffer buf, ProtocolDecoderOutput out) throws Exception {
    MyBuffer myBuf=new MyBuffer(buf);
    if(myBuf.remaining() > 0) {// 有数据时，读取包头
      myBuf.mark();// 先标记，以便数据不完整时恢复
      String fileName=myBuf.getString();
      int length=myBuf.getInt();
      int pos=myBuf.getInt();
      if(myBuf.remaining() < length) {// 数据长度不够
        System.out.println("数据长度不完整,拼凑完整数据!!!!!!!!!!!!!!!");
        myBuf.reset();// 恢复
        return false;// 接收新数据，拼凑完整数据
      } else {// 有完整数据包
      // System.out.println("有可解析的完整包");
        byte[] b=new byte[length];
        myBuf.get(b);
        IoBuffer data=IoBuffer.allocate(length);
        data.put(b);
        data.flip();
        // 封装对象
        MyFile myFile=new MyFile();
        myFile.setName(fileName);
        myFile.setLength(length);
        myFile.setPos(pos);
        myFile.setData(data);
        out.write(myFile);
        // 解析完一个包后发现粘包了，再进行下一次解析
        if(myBuf.remaining() > 0) {
          System.out.println("粘包");
          return true;
        }
      }
    }
    return false;// 处理成功，接收下个包
  }

  public void setCharset(Charset charset) {
    this.charset=charset;
  }

  public Charset getCharset() {
    return charset;
  }
}
/*
 * return true; 首先判断你是否在 doDecode()方法中从内部的 IoBuffer 缓冲区读取了数据， 如果没有，则会抛出非法的状态异常， 也就是你的 doDecode()方法返回 true 就表示你已经消费了本次数据
 * （相当于聊天室中一个完整的消息已经读取完毕），进一步说，也就是此时你必须已经消费过内部的 IoBuffer 缓冲区的数据（哪怕是消费了一个字节的数据）。
 * 如果验证过通过，那么 CumulativeProtocolDecoder 会检查缓冲区内是否还有数据未读取，如果有就继续调用 doDecode()方法，没有就停止对 doDecode()方法的调用，直到有新的数据被缓冲
 */
/*
 * return false; 会停止对doDecode()的调用，如果还有剩余数据则将数据缓存到session中以便下次提取使用
 */
